home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / revgrep.cq / revgrep.c
Text File  |  1986-03-11  |  20KB  |  759 lines

  1.  
  2. Relay-Version: version B 2.10.2 9/5/84; site philabs.UUCP
  3. Path: philabs!cmcl2!harvard!talcott!panda!genrad!decvax!bellcore!ulysses!mhuxr!mhuxn!ihnp4!wucs!nz
  4. From: nz@wucs.UUCP (Neal Ziring)
  5. Newsgroups: net.sources
  6. Subject: reverse grep (now sysV compatible!)
  7. Message-ID: <1458@wucs.UUCP>
  8. Date: 25 Feb 86 17:41:34 GMT
  9. Date-Received: 27 Feb 86 05:07:30 GMT
  10. Organization: Washington U. Engineering Computer Lab
  11. Lines: 742
  12. Keywords: grep search filter tail source
  13.  
  14. <<I tried sending this to mod.sources, but it couldn't get there>>
  15.  
  16. Grep is one of the most useful filters around, but sometimes you
  17. are more interested in what is going on near the end of a file than
  18. you are in what is going on at the front.  Grep, and most other filters,
  19. look at the front of a file before proceeding to its end.  Log files
  20. are often most interesting near the end.
  21.  
  22. Revgrep behaves very much like grep, except that it reads lines of a
  23. file in reverse order -- end towards beginning.  Also, revgrep subsumes
  24. the functionality of tail, without tail's length restrictions.
  25.  
  26. I hope Berkeley and SystemV users find revgrep as useful as I do.
  27.  
  28. ...nz (Neal Ziring @ Washington University Engineering Computer Lab)
  29.  
  30. --------------------
  31. #! /bin/sh
  32. # This is a shell archive, meaning:
  33. # 1. Remove everything above the #! /bin/sh line.
  34. # 2. Save the resulting text in a file.
  35. # 3. Execute the file with /bin/sh (not csh) to create the files:
  36. #    README
  37. #    Makefile
  38. #    revgrep.1
  39. #    revgrep.c
  40. # This archive created: Mon Feb 24 09:17:31 1986
  41. # By:    Neal Ziring (Washington U. Engineering Computer Lab)
  42. export PATH; PATH=/bin:$PATH
  43. if test -f 'README'
  44. then
  45.     echo shar: will not over-write existing file "'README'"
  46. else
  47. cat << \SHAR_EOF > 'README'
  48. The Makefile for revgrep is designed for Berkeley Unix, to change it for
  49. AT&T system V, change the CFLAGS and LIBES variables as shown below.
  50.  
  51. CFLAGS= -DUSG
  52. LIBES= -lPW
  53.  
  54. Send any complaints or suggestions about revgrep to nz@wucs.UUCP .
  55.  
  56. SHAR_EOF
  57. fi # end of overwriting check
  58. if test -f 'Makefile'
  59. then
  60.     echo shar: will not over-write existing file "'Makefile'"
  61. else
  62. cat << \SHAR_EOF > 'Makefile'
  63. #
  64. # revgrep: search files for a pattern, backwards
  65. #
  66. #     Revgrep used to be composed of three source files, 
  67. #   revgrep.c, revio.c, and revgrep.h.  Since none of the
  68. #   three were particularly big, they were combined into 
  69. #   one source file, revgrep.c.
  70. #
  71. SRCS= revgrep.c
  72. OBJS= revgrep.o 
  73. CFLAGS= -O
  74. LIBES=
  75.  
  76. DESTDIR= /usr/local/bin
  77.  
  78. all: revgrep
  79.  
  80. install: revgrep
  81.     install -s -m 755 revgrep ${DESTDIR}
  82.  
  83. revgrep: ${OBJS}
  84.     cc ${CFLAGS} -o revgrep ${OBJS} ${LIBES}
  85.  
  86. clean: 
  87.     rm -f revgrep.o a.out
  88. SHAR_EOF
  89. fi # end of overwriting check
  90. if test -f 'revgrep.1'
  91. then
  92.     echo shar: will not over-write existing file "'revgrep.1'"
  93. else
  94. cat << \SHAR_EOF > 'revgrep.1'
  95. .TH REVGREP 1 "7 September 1985"
  96. .UC 4
  97. .SH NAME
  98. revgrep \- search a file for a pattern backwards, provide tails
  99. .SH SYNOPSIS
  100. .B revgrep
  101. [ option ] ...
  102. expression [ file ] ...
  103. .SH DESCRIPTION
  104. .I Revgrep
  105. is a variant of the 
  106. .I grep
  107. family.
  108. The named
  109. .I files
  110. (standard input default)
  111. are searched from the end towards the beginning
  112. for occurences of the specified pattern.
  113. The patterns recognized are limited regular expressions
  114. of the type described the manual entry for
  115. .IR ed (1).
  116. The library routine
  117. .IR regex (3)
  118. is used to do pattern matching.
  119. .PP
  120. .I Revgrep 
  121. can also be used to print a trailing portion of a file, either based on 
  122. the occurence of a pattern, or on a number of lines.  In this function it
  123. is subsumed by 
  124. .IR sed (1)
  125. and
  126. .IR tail (1), 
  127. and is much faster than 
  128. .I sed(1)
  129. for long files.
  130. .PP
  131. The following options are recognized.
  132. .TP
  133. .B \-v
  134. All lines but those matching are printed.
  135. .TP
  136. .B \-c
  137. Only a count of matching lines is printed.
  138. .TP
  139. .B \-l
  140. The names of files with matching lines are listed (once) separated by newlines.
  141. .TP
  142. .B \-i
  143. Upper case is folded to lower case for comparisons.
  144. .TP
  145. .B \-n
  146. Each line is preceded by its relative line number from the end of the file.
  147. .TP
  148. .B \-b
  149. Each line is preceded by the block number on which it was found.
  150. This is sometimes useful in locating disk block numbers by context.
  151. .TP
  152. .B \-s
  153. Silent mode.  Nothing is printed (except error messages).
  154. This is useful for checking the error status.
  155. .TP
  156. .BI \-e " expression"
  157. Same as a simple
  158. .I expression 
  159. argument, but useful when the
  160. .I expression
  161. begins with a \-.
  162. .TP
  163. .B \-y
  164. Same as 
  165. .B \-i.
  166. .TP
  167. .B \-t
  168. Give a file tail whose first line matches the given pattern.
  169. There is no limit on the length of the tail.
  170. .TP
  171. .I \-cnt
  172. Give a file tail \fIcnt\fP lines long.
  173. This performs the same function as
  174. .IR tail (1)
  175. but is much less efficient for operations on pipes.  There is no
  176. limit on the length of the file tail (unlike 
  177. .I tail,
  178. which limits the total length of the file tail to 4096 characters).
  179. .LP
  180. In all cases the file name is shown if there is more than one input file.
  181. Care should be taken when using the characters $ * [ ^ | ( ) and \\ in the
  182. .I expression
  183. as they are also meaningful to the Shell.  It is safest to enclose the entire
  184. .I expression
  185. argument in single quotes \' \'.
  186. .LP
  187. In the following description of regular expression
  188. syntax, `character' excludes newline:
  189. .IP
  190. A \e followed by a single character other than newline matches that character.
  191. .IP
  192. The character ^ matches the beginning of a line.
  193. .IP
  194. The character $ matches the end of a line.
  195. .IP
  196. .B .
  197. (period) matches any character.
  198. .IP
  199. A single character not otherwise endowed with special
  200. meaning matches that character.
  201. .IP
  202. A string enclosed in brackets [\|] matches any single character from the string.
  203. Ranges of ASCII character codes may be abbreviated as in `a\-z0\-9'.
  204. A ]
  205. may occur only as the first character of the string.
  206. A literal \- must be placed where it can't be mistaken as a range indicator.
  207. .IP
  208. A regular expression followed by an * (asterisk) matches a sequence of 0
  209. or more matches of the regular expression.
  210. A regular expression followed by a + (plus) matches a sequence of 1 or more
  211. matches of the regular expression.
  212. A regular expression followed by a ? (question mark) matches a sequence of
  213. 0 or 1 matches of the regular expression.
  214. .IP
  215. Two regular expressions concatenated match a match of the first followed
  216. by a match of the second.
  217. .IP
  218. A regular expression enclosed in quoted parentheses
  219. matches a match for the regular expression (e.g. \e( \fIx\fP \e)).
  220. .IP
  221. A number \fIn\fP preceded by a backslash \e matches the same string
  222. as the \fIn\fPth
  223. regular expression enclosed in parentheses.
  224. .LP
  225. The order of precedence of operators at the same parenthesis level
  226. is [\|] then *+? then concatentation.
  227. .SH "SEE ALSO"
  228. ed(1),
  229. sed(1),
  230. sh(1),
  231. grep(1),
  232. bm(1),
  233. tail(1),
  234. body(1)
  235. .SH DIAGNOSTICS
  236. Exit status is 0 if any matches are found,
  237. 1 if none, 2 for syntax errors or inaccessible files.
  238. .SH BUGS
  239. Lines are limited to 2047 characters; longer lines are split into
  240. chunks at most 2047 characters long.
  241. .PP
  242. Data from non-seekable descriptors (pipes, sockets, and ttys)
  243. is copied to a temporary file, ``/tmp/rvg.*''.
  244. This file will not be removed if the
  245. program is killed with a SIGKILL or a SIGTERM.
  246. SHAR_EOF
  247. fi # end of overwriting check
  248. if test -f 'revgrep.c'
  249. then
  250.     echo shar: will not over-write existing file "'revgrep.c'"
  251. else
  252. cat << \SHAR_EOF > 'revgrep.c'
  253.  
  254. static char rcsid[] = " $Header: revgrep.c,v 1.4 86/02/24 09:10:37 nz Exp $";
  255.  
  256. /* **************************************************************
  257.  * revgrep: search files for a pattern backwards
  258.  *
  259.  *        revgrep is a variant of the grep(1) family, designed
  260.  *    to behave like grep, but do all line input in reverse.
  261.  *    (regular expression are matched left-to-right, as usual.)
  262.  *    the library routines re_comp and re_exec are the basis for
  263.  *    the regular-expression matching.
  264.  *
  265.  *    in addition to the functionality of grep, revgrep provides
  266.  *    the functionality of tail(1), but with the option to base
  267.  *    the length of the tail on a match of a regular expression
  268.  *    as well as on the line count.  further, revgrep does not
  269.  *    have tail's buffer-size limitations (at the price of being
  270.  *    a little slower).
  271.  *
  272.  *    the basic calling syntax for revgrep is as follows:
  273.  *
  274.  *            % revgrep [ options ] [ expression ] [ file ... ]
  275.  *
  276.  *    if no files are given, revgrep reads the std. input.
  277.  *    meaningful options are:
  278.  *
  279.  *           -v        print only lines that don't match
  280.  *           -c         print only a count of matching lines
  281.  *           -l         print only filenames of files with matches
  282.  *           -n         print relative line number with matches
  283.  *           -b        print block number in file with matches
  284.  *           -s         no output, only status
  285.  *           -h         don't print filenames with matches
  286.  *           -y         fold upper case to lower on input
  287.  *           -e         next argument is the expression
  288.  *
  289.  *           -t         text from first match (backwards) to
  290.  *                      end is printed in correct order.
  291.  *           -nnn       forego matches, do a -t on this many lines
  292.  */
  293.  
  294. #include <stdio.h>
  295. #include <ctype.h>
  296. #include <signal.h>
  297. #ifdef USG
  298. #include <sys/types.h>
  299. #include <string.h>
  300. #include <fcntl.h>
  301. #else
  302. #include <strings.h>
  303. #endif
  304. #include <sys/param.h>
  305. #include <sys/stat.h>
  306. #include <sys/file.h>
  307.  
  308. #ifdef USG
  309. #define RBUF 1024
  310. char *compiled_re, *regex();
  311. #else
  312. #define RBUF 2048
  313. #endif
  314.  
  315. #define NO_FILE_FLAG -10
  316.  
  317. int rev_fd = -1;
  318. int sty    =  0;
  319. int fflag  =  1;
  320. extern int errno;
  321. int exitval = 1;
  322. #ifdef USG
  323. #define Is_nonseek(FD,SB) \
  324.   ( (SB.st_mode == 0) || (SB.st_mode & S_IFMT == S_IFIFO) || (isatty(FD))  )
  325. #else
  326. #define Is_nonseek(FD,SB) \
  327.   ( (SB.st_mode == 0) || (SB.st_mode & S_IFMT == S_IFSOCK) || (isatty(FD)) )
  328. #endif USG
  329.  
  330.  
  331. char dont_match = 0;
  332. char print_count= 0;
  333. char print_names= (char) 4;
  334. char print_blocks=0;
  335. char print_lnum = 0;
  336. char print_matches= 1;
  337. char fold_upper = 0;
  338. char use_stdin  = 1;
  339. char do_tail    = 0;
  340. char silent_mode= 0;
  341. int  tail_cnt   = 0;
  342. char *tmp_name   = NULL;
  343.  
  344.  main(argc,argv)
  345.      int argc;
  346.      char *argv[];
  347. {
  348.     char *s1, *s2;
  349.     char *expr = NULL;
  350.     int i;
  351.     struct stat sb;
  352.  
  353.     /* first try to process options */
  354.     for(s1 = *++argv, argc--; argc > 0 && s1 != NULL; s1 = *++argv, argc--) {
  355.     if (*s1 != '-') break;
  356.     for(s2=s1; *++s2 != '\0'; ) {
  357.  
  358.         switch (*s2) {
  359.           case 'v':
  360.         dont_match++;
  361.         break;
  362.           case 'c':
  363.         print_count++;
  364.         print_matches=0;
  365.         break;
  366.           case 'n':
  367.         print_lnum = 1;
  368.         break;
  369.           case 'l':
  370.         print_names = 1;
  371.         print_matches=0;
  372.         break;
  373.           case 'b':
  374.         print_blocks++;
  375.         break;
  376.           case 's':
  377.         print_names = 0;
  378.         print_matches=0;
  379.         print_blocks = 0;
  380.         print_count = 0;
  381.         do_tail     = 0;
  382.         silent_mode = 1;
  383.         break;
  384.           case 'h':
  385.         print_names = 0;
  386.         break;
  387.           case 'i':
  388.           case 'y':
  389.         fold_upper = 1;
  390.         break;
  391.           case 't':
  392.         do_tail = 1;
  393.         print_matches = 0;
  394.         break;
  395.           case 'e':
  396.         expr = *++argv;
  397.         break;
  398.           default:
  399.         if ( isdigit(*s2) ) {
  400.             do_tail = 1;
  401.             print_matches = 0;
  402.             tail_cnt = atoi(s2);
  403.             for( ; *s2 != '\0' ; s2++);
  404.             s2--;
  405.         }
  406.         else {
  407.             if (silent_mode) exit(2);
  408.             fprintf(stderr,"revgrep: Unknown flag, '%c'.\n",*s2);
  409.             fprintf(stderr,
  410.                " usage: revgrep [ -slbchnyet# ] expr [ file ] ...\n");
  411.             exit(2);
  412.         }
  413.         }
  414.     }
  415.     }
  416.  
  417.     if (s1 != NULL && expr == NULL && tail_cnt == 0) {
  418.     expr = s1;
  419.     argv++; argc--;
  420.     }
  421.     if (expr == NULL && tail_cnt == 0) exit(2);
  422.     if (expr != NULL) {
  423. #ifdef USG
  424.     char *regcmp();
  425.     if ( (compiled_re = regcmp(expr,NULL)) == NULL) {
  426.         if ( ! silent_mode ) fprintf(stderr,"revgrep: bad expression.\n");
  427.         exit(2);
  428.     }
  429. #else
  430.     char *re_comp();
  431.     if ( (s1 = re_comp(expr)) != NULL) {
  432.         if ( ! silent_mode ) fprintf(stderr,"revgrep: %s \n",s1);
  433.         exit(2);
  434.     }
  435. #endif
  436.     }
  437.  
  438.     if (print_names > 1  &&
  439.     ( (*argv == NULL)?(1):(*(argv+1) == NULL))) print_names = 0;
  440.  
  441.     if (*argv == NULL) {
  442.     rev_fd = 0;
  443.     sty = 0;
  444.     if ( fstat(0,&sb) >= 0 ) {
  445.         if ( Is_nonseek(0,sb) )
  446.           {
  447.         if (  tmp_setup(0) < 0 ) {
  448.             if ( ! silent_mode ) perror("revgrep: tmp_setup");
  449.             exit(2);
  450.         }
  451.           }
  452.     }
  453.     else {
  454.         exit(2);
  455.     }
  456.     }
  457.     i = rgrep(argv);
  458.  
  459.     if ( tmp_name != NULL )
  460.       unlink(tmp_name);
  461.  
  462.     exit(i);
  463. }
  464.  
  465.  
  466. /* **************************************************************
  467.  * tail: print a tail, given a file position
  468.  *
  469.  *       This routine copies to stdout all the data after a
  470.  *   certain file position.  It checks various global flags
  471.  *   before starting the copy, and prints a message if
  472.  *   appropriate.  The file of which a tail is to be done
  473.  *   is assumed to be open on rev_fd.
  474.  */
  475.  tail(fpos,fname)
  476.      int fpos;
  477.      char *fname;
  478. {
  479.     int len;
  480.     char buf[RBUF];
  481.  
  482.     if (print_names) printf("%s:",fname);
  483.     if (print_blocks) printf("starting in %d",fpos/512);
  484.     if (print_names || print_blocks) putchar('\n');
  485.  
  486.     if (lseek(rev_fd,(fpos==0)?(0):(fpos+1),0) < 0) {
  487.     perror("revgrep: tail");
  488.     return(-1);
  489.     }
  490.     while( (len=read(rev_fd,buf,RBUF)) > 0)
  491.     fwrite(buf,sizeof(char),len,stdout);
  492.  
  493.     return(0);
  494. }
  495.  
  496.  
  497.  
  498. /* **************************************************************
  499.  * rgrep: backwards grep a set of files, or stdin
  500.  *
  501.  *      Rgrep performs a backwards grep on an input vector
  502.  *   of files.  If the input vector is Null, then the stdin
  503.  *   is assumed to be the input file.
  504.  *   This routine uses rline() to get the next line backwards,
  505.  *   and uses doline() to do pattern-matching and output on
  506.  *   the line received.
  507.  */
  508.  rgrep(files)
  509.      char *files[];
  510. {
  511.     char *s1, *s2;
  512.     char *lbuf;
  513.     int  pos, lcnt, match, match_cnt;
  514.     char didone = 0;
  515.  
  516.     for(didone = 0;
  517.     didone == 0 || *files != NULL;
  518.     files += ((*files==NULL)?0:1))
  519.     {
  520.     char *f, *rline();
  521.     f = *files;
  522.     for(lcnt=0, match_cnt=0, didone = 1;
  523.         ( lbuf = rline(&pos,f) ) != NULL;
  524.         f=NULL, lcnt++)
  525.       {
  526.           if (match = doline(lbuf,pos,lcnt,*files)) {
  527.           match_cnt++;
  528.           exitval=0;
  529.           if (do_tail) {
  530.               tail(pos,*files);
  531.               didone = 1;
  532.               break;
  533.           }
  534.           if (print_names && print_matches == 0 && print_count == 0) {
  535.               printf("%s\n",*files);
  536.               break;
  537.           }
  538.           }
  539.       }
  540.     if (print_count) {
  541.         if (print_names) printf("%s:%d\n",*files,match_cnt);
  542.         else printf("%d\n",match_cnt);
  543.     }
  544.     }
  545.     return(exitval);
  546. }
  547.  
  548. /* **************************************************************
  549.  * doline: match and handle a single line of input
  550.  *
  551.  *       This line receives a single text line, and performs
  552.  *    pattern matches on it if necessary.  Depending on the
  553.  *    various options, the line may be output with a message
  554.  *    two.
  555.  *       When a match of any kind occurs, 1 is returned.
  556.  *    Otherwise 0 is returned.
  557.  */
  558.  doline(lbuf,pos,lin,fname)
  559.      char *lbuf;
  560.      int pos, lin;
  561.      char *fname;
  562. {
  563.     register char *s1, *s2;
  564.     int i,j,m;
  565.  
  566.     if (tail_cnt > 0) {
  567.         if (lin >= tail_cnt || pos <= 1) return(1);
  568.         else return(0);
  569.     }
  570.  
  571.     if (fold_upper) {
  572.         char cmpbuf[RBUF*2];
  573.         for(s1=lbuf, s2=cmpbuf;
  574.         *s1 != '\0' && (s2-cmpbuf) < (RBUF*2); s1++, s2++ )
  575.           *s2 = (isupper(*s1))?(tolower(*s1)):(*s1);
  576.         *s2 = '\0';
  577.         s1 = cmpbuf;
  578.     }
  579.     else  s1 = lbuf;
  580.  
  581. #ifdef USG
  582.     m = (regex(compiled_re,s1)==NULL)?(0):(1);
  583. #else
  584.     m = re_exec(s1);
  585. #endif
  586.  
  587.     m = (dont_match)?(! m):(m);
  588.     if (! m) return(0);
  589.  
  590.     if (print_matches) {
  591.     if (print_names) printf("%s:",fname);
  592.     if (print_lnum) printf("-%d:",lin);
  593.     if (print_blocks) printf("%d:",pos/512);
  594.     printf("%s\n",lbuf);
  595.     }
  596.     return(1);
  597. }
  598.  
  599.  
  600. /* **************************************************************
  601.  * tmp_setup: read a non-seekable channel into a temp file
  602.  *
  603.  *       This routine reads the contents of a file descriptor
  604.  *  into a temporary file, and then passes back the name of the
  605.  *  file in a NULL-terminated vector.
  606.  *
  607.  *       A signal catcher is set up to delete the file if
  608.  *  the user hits ^C or ^\
  609.  */
  610.  tmp_setup(ifd)
  611.      int ifd;
  612. {
  613.     static char nambuf[60];
  614.     int ofd, len;
  615.     int sig_exit();
  616.     char dbuf[RBUF];
  617.  
  618.     sprintf(nambuf,"/tmp/rvg.a%d",getpid());
  619.     if ( (ofd = open(nambuf,O_RDWR | O_CREAT,0600)) < 0) return(-1);
  620.  
  621.     if ( signal(SIGINT,sig_exit) == SIG_IGN) signal(SIGINT,SIG_IGN);
  622.     if ( signal(SIGQUIT,sig_exit) == SIG_IGN) signal(SIGQUIT,SIG_IGN);
  623.     if ( signal(SIGHUP,sig_exit) == SIG_IGN) signal(SIGHUP,SIG_IGN);
  624.  
  625.     for( ; (len = read(ifd,dbuf,RBUF)) > 0; write(ofd,dbuf,len));
  626.     tmp_name = nambuf;
  627.     rev_fd = ofd;
  628.     return (ofd);
  629. }
  630.  
  631. sig_exit()
  632. {
  633.     close(0); close(1); close(2); close(3); close(4); close(5); close(6);
  634.     close(rev_fd);
  635.     if (tmp_name != NULL) unlink(tmp_name);
  636.     exit(2);
  637. }
  638.  
  639.  
  640.  
  641. /* **************************************************************
  642.  * rline: get next line going backwards, return pertinent info
  643.  *
  644.  *        This routine returns the next line going backwards from
  645.  *   the end of a file.  It also will open a new file, if
  646.  *   neccesary, and if given a file name as a parameter.
  647.  *
  648.  *   The line is written into a static buffer, and a pointer to
  649.  *   that small buffer is returned.  Also, the position in the
  650.  *   file of the start of the line is returned.
  651.  */
  652.  char *rline(posp, fname)
  653.      int *posp;
  654.      char *fname;
  655. {
  656.     register char  *s1, *s2;
  657.     static char *bufp;
  658.     static char bf[RBUF];
  659.     static char lbf[RBUF * 2], rlbf[RBUF * 2];
  660.     static int  bf_len, fpos;
  661.  
  662.     if (fname != NULL || fflag) {
  663.         if ( (bf_len = rblock(&fpos, bf, fname)) < 0) return(NULL);
  664.         bufp = bf + (bf_len - 1);
  665.         fflag = 0;
  666.     }
  667.  
  668.     /* by now we have a block, so search backwards for newline */
  669.     for(s1=lbf, s2=s1 + (2*RBUF - 1), *s2-- = '\0';
  670.         *bufp != '\n' &&   s2 >= s1;
  671.         bufp--, fpos--)
  672.       {
  673.         *s2-- = *bufp;
  674.         if (bufp == bf) {
  675.             if ((bf_len = rblock(&fpos,bf,NULL)) < 0) {
  676.                 fflag = 1;
  677.                 if (bf_len == NO_FILE_FLAG)
  678.                   return(NULL);
  679.                 break;
  680.             }
  681.             bufp = bf + bf_len;
  682.             fpos++;
  683.         }
  684.     }
  685.     fpos--;
  686.     if ( --bufp < bf ) fflag = 1;
  687.     *posp = fpos;
  688.  
  689.     return(++s2);
  690. }
  691.  
  692. /* **************************************************************
  693.  * rblock: return next block going backward in a file
  694.  *
  695.  *       This routine reads the next-last block in a file,
  696.  *    or starts a new file, given a name.
  697.  *    The flag  sty  tells whether the current file descriptor
  698.  *    has been stat-ed yet.  If not, it is implicit that the
  699.  *    blocks will be taken from the end of the file.
  700.  *
  701.  *       Note, if this routine fails with errors 29 or 5 then
  702.  *    a message is printed and the whole program aborts.
  703.  *
  704.  *      The length of the block read-and-returned is returned.
  705.  */
  706.  rblock(fposp, buf, fname)
  707.      int *fposp;
  708.      char *buf, *fname;
  709. {
  710.     static int nbp;
  711.     int    len;
  712.  
  713.     if (fname != NULL) {
  714.         if ( rev_fd > -1 ) close(rev_fd);
  715.         if ( (rev_fd=open(fname,O_RDONLY,0)) < 0) {
  716.             fprintf(stderr,"revgrep: ");
  717.             perror(fname);
  718.             return(NO_FILE_FLAG);
  719.         }
  720.         sty = 0;
  721.     }
  722.  
  723.     if (! sty) {
  724.         struct stat sb;
  725.         if (fstat(rev_fd, &sb) < 0) return(-1);
  726.         nbp = (sb.st_size  / RBUF ) * RBUF;
  727.         sty = 1;
  728.     }
  729.  
  730.     if (nbp < 0) return(-1);
  731.     if ( lseek(rev_fd,nbp,0) < 0) {
  732.         if (errno == 29  ||  errno == 5) {
  733.             perror("revgrep: rblock");
  734.             exit(1);
  735.         }
  736.         else return(-1);
  737.     }
  738.  
  739.     if ( (len = read(rev_fd,buf,RBUF)) < 0) return(-1);
  740.     *fposp = nbp + len;
  741.     nbp = nbp - RBUF;
  742.     return( len );
  743. }
  744. SHAR_EOF
  745. fi # end of overwriting check
  746. #    End of shell archive
  747. exit 0
  748.  
  749. ------------
  750. -- 
  751. ...nz@wucs (ECL - we're here to provide superior computing)
  752.  
  753.     "We can fit an infinite number of wires into this ... junction box,
  754.         ... but we don't do that far in practice"
  755.  
  756.  
  757. % ine */
  758.     f